﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for TextProximity
/// </summary>
public class TextGroup
{
    /// <summary>
    /// If our string has sentences then we want to analyze proximity
    /// based on whether or not two words are in the same sentence.
    /// </summary>
    private List<string> sentences;
    /// <summary>
    /// The entire string that is being analyzed. We want to store this
    /// as we will still consider cross sentance relationships however
    /// the weight of their relationship is lower than that of same sentence
    /// relationships.
    /// </summary>
    private string entireString;
    /// <summary>
    /// The words in the string in the order which they occur in the string.
    /// </summary>
    private List<string> words;

    public TextGroup(string str)
    {
        string input = str.ToLower();
        sentences = new List<string>(input.Split('.').Length);
        foreach (string sentence in input.Split('.'))
        {
            sentences.Add(sentence);
        }
        entireString = (string)input.Clone();
        //By default splits on whitespace.
        words = new List<string>(input.Split());
    }

    /// <summary>
    /// Get the related words and their associated weights within this
    /// text for the given string.
    /// </summary>
    /// <param name="str">A word that is contained within</param>
    /// <returns>A dictionary keyed on the word in the string with the
    /// value being the weight of the relationship to the passed in
    /// string</returns>
    public Dictionary<string, int> getRelatedWordsAndWeights(string str)
    {
        Dictionary<string, int> relatedWordsAndWeights = new Dictionary<string, int>(words.Count());
        if (words.Contains(str))
        {
            determineWeightBySentence(ref relatedWordsAndWeights, str);
        }
        return relatedWordsAndWeights;
    }

    /// <summary>
    /// Determine the weights of the words in the sentences in this text group
    /// in relation to the passed in word.
    /// </summary>
    /// <param name="relatedWordsAndWeights">Dictionary with the words in the
    /// text group as keys and their relationship to the desired word as values.</param>
    /// <param name="str"></param>
    private void determineWeightBySentence(
        ref Dictionary<string, int> relatedWordsAndWeights, string str)
    {
        if (relatedWordsAndWeights == null)
        {
            relatedWordsAndWeights = new Dictionary<string, int>(words.Count());
        }

        foreach (string sentence in sentences)
        {
            string[] wordsInSentence = sentence.Split();
            if (wordsInSentence.Contains(str))
            {
                //Since we could have the word multiple times in the sentence we
                //want to consider the proximity for each word.
                foreach (int indexOfWord in indexesOf(ref wordsInSentence, str))
                {
                    int index = 0;
                    foreach (string wordInSentence in wordsInSentence)
                    {
                        if (wordInSentence.Length < 2)
                        {
                            continue;
                        }
                        //Evaluate how "far away" the sentence word is from
                        //the desired word.
                        int distance = Math.Abs(indexOfWord - index);
                        addWordWeight(ref relatedWordsAndWeights, wordInSentence, distance);
                        index++;
                    }
                }

            }
        }
    }

    /// <summary>
    /// Get the indexes in which the itemToLookFor exists in the string array.
    /// </summary>
    /// <param name="array">An array that may or may not have the specified
    /// item in it.</param>
    /// <param name="itemToLookFor">The item that is being looked for in the array.
    /// </param>
    /// <returns>The indices where the item exists.</returns>
    private int[] indexesOf(ref string[] array, string itemToLookFor)
    {
        int[] bigIndices = new int[array.Length];
        int currentIndex = 0;
        int numberOfIndices = 0;
        foreach (string item in array)
        {
            if (item.ToLower().Equals(itemToLookFor.ToLower()))
            {
                bigIndices[numberOfIndices] = currentIndex;
                ++numberOfIndices;
            }
            ++currentIndex;
        }
        int[] indices = new int[numberOfIndices];
        Array.Copy(bigIndices, indices, numberOfIndices - 1);
        return indices;
    }

    /// <summary>
    /// Add the weight of the passed in word to the dictionary if it isn't
    /// there and if it is then increment the weight.
    /// </summary>
    /// <param name="relatedWordsAndWeights">Dictionary containing the 
    /// words and their associated weights.</param>
    /// <param name="key"></param>
    /// <param name="value"></param>
    private void addWordWeight(ref Dictionary<string, int> relatedWordsAndWeights,
        string key, int value)
    {
        if (relatedWordsAndWeights.ContainsKey(key))
        {
            relatedWordsAndWeights[key] = relatedWordsAndWeights[key] + value;
        }
        else
        {
            relatedWordsAndWeights.Add(key, value);
        }
    }
}